package com.mokairui.share_model_nolock;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @Description 函数式接口:
 *      supplier: 生产者 () -> 结果
 *      function: 函数   (参数) -> 结果, BiFunction(参数1, 参数2) -> 结果
 *      consumer: 消费者  (一个参数没有结果) (参数) -> void, BiConsumer(参数1, 参数2) -> void
 * @Author Mokairui
 * @Since 2021/10/5
 */
@Slf4j(topic = "c.functionInterface")
public class FunctionalInterface {
    public static void main(String[] args) {
        // 数组不安全, 结果不正确
        demo(
                () -> new int[10],
                (array) -> array.length,
                (array, index) -> array[index]++,
                array -> System.out.println(Arrays.toString(array))
        );

        demo(
                () -> new AtomicIntegerArray(10),
                (array) -> array.length(),
                (array, index) -> array.getAndIncrement(index),
                array -> System.out.println(array)
        );
    }

    /**
     *
     * @param arraySupplier 提供数组, 可以是线程不安全数组或线程安全数组
     * @param lengthFun 获取数组的长度的方法
     * @param putConsumer 自增方法, 回转array, index
     * @param printConsumer 打印数组的方法
     * @param <T>
     */
    private static <T> void demo(
            Supplier<T> arraySupplier,
            Function<T, Integer> lengthFun,
            BiConsumer<T, Integer> putConsumer,
            Consumer<T> printConsumer) {
        List<Thread> ts = new ArrayList<>();
        T array = arraySupplier.get();
        int length = lengthFun.apply(array);
        for (int i = 0; i < length; i++) {
            ts.add(new Thread(() -> {
                // 每个线程对数组做10000次操作
                for (int j = 0; j < 10000; j++) {
                    putConsumer.accept(array, j % length);
                }
            }));
        }

        ts.forEach(Thread::start);
        // 等待所有线程结束
        ts.forEach(it -> {
            try {
                it.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        printConsumer.accept(array);
    }
}
